/*------------------------------------------------------------------------
|                             FILE DESCRIPTION                           |
------------------------------------------------------------------------*/
/*------------------------------------------------------------------------
|  - File name     : os_mem.c
|  - Author        : zeweni
|  - Update date   : 2021.05.05
|  - Copyright(C)  : 2021-2021 zeweni. All rights reserved.
------------------------------------------------------------------------*/
/*------------------------------------------------------------------------
|                            COPYRIGHT NOTICE                            |
------------------------------------------------------------------------*/
/*
 * Copyright (C) 2021, zeweni (17870070675@163.com)

 * This file is part of Ant Real Time Operating System.

 * Ant Real Time Operating System is free software: you can redistribute 
 * it and/or modify it under the terms of the Apache-2.0 License.

 * Ant Real Time Operating System is distributed in the hope that it will 
 * be useful,but WITHOUT ANY WARRANTY; without even the implied warranty 
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * Apache-2.0 License License for more details.

 * You should have received a copy of the Apache-2.0 License.Ant Real Time
 * Operating System. If not, see <http://www.apache.org/licenses/>.
**/
/*------------------------------------------------------------------------
|                               INCLUDES                                 |
------------------------------------------------------------------------*/

#include "os_mem.h"
#include "os_cpu.h"

/*------------------------------------------------------------------------
|                             DECLARATION                                |
------------------------------------------------------------------------*/

/**
 * @brief     地址向高地址偏移。
 * @details   The address is shifted to the higher address.
**/
#define ADDR_HIGH_OFFSET(addr,length)  do{(addr) += (length);}while(0)

/**
 * @brief     地址向低地址偏移。
 * @details   The address is shifted to the lower address.
**/
#define ADDR_LOW_OFFSET(addr,length)   do{(addr) -= (length);}while(0)

/*------------------------------------------------------------------------
|                                  DATA                                  |
------------------------------------------------------------------------*/

/**
 * @brief     内存池结构体类型。
 * @details   Memory pool structure type..
**/
struct Mem_Pool
{
    os_vuint8_t *ReMemAddr;  /*!< 当前空闲内存块地址。 */
    os_vuint8_t *ReInfoAddr;  /*!<当前空闲内存块信息地址。 */
	os_vuint16_t MemBlkCnt;  /*!< 申请的内存块数量。 */
    os_vuint16_t ReLength;    /*!< 空闲内存块长度。 */
    os_vuint8_t Pool[OS_MEM_POOL_SIZE];  /*!< 内存池。Memory pool*/
};

/**
 * @brief     定义内存池,声明储存在XRAM当中。
 * @details   Define memory pool,the statement is stored in XRAM.
**/
static xdata struct Mem_Pool G_MemPool;

/**
 * @brief     内存块信息结构体类型。
 * @details   Memory block information structure type.
 * @note      内存块信息储存在内存池的顶部，向下生长。
 *            内存块储存在内存池的底部，向上生长。
 *            The memory block information is stored 
 *            at the top of the memory pool and grows downward.
 *            The memory block is stored 
 *            at the bottom of the memory pool and grows upward.
**/
struct Mem_Block
{
	volatile void **MalPtr;   /*!< 要申请内存的指针地址。*/
	os_vuint8_t *IndexAddr; /*!< 内存块首地址。 */
    os_vuint16_t Length;  /*！< 内存块长度。*/
};

/**
 * @brief     内存回收器结构体。
 * @details   Memory collector structure.
 * @note      内存块信息储存在内存池的顶部，向下生长。
 *            内存块储存在内存池的底部，向上生长。
 *            The memory block information is stored 
 *            at the top of the memory pool and grows downward.
 *            The memory block is stored 
 *            at the bottom of the memory pool and grows upward.
**/
struct RecMem_Type
{
    void *MPtr[OS_THREAD_MAX_NUM];
    os_uint8_t Num;
};

static struct RecMem_Type G_RecMem_Collector = {0};


#define MEM_BLOK_INFO_SIZE   (sizeof(struct Mem_Block))

/*------------------------------------------------------------------------
|                                FUNCTION                                |
------------------------------------------------------------------------*/

/**
 * @brief   内存初始化函数，对内存池进行初始化，
 *          分静态和动态两部分，内核主要使用静态方法。
 * @details The memory initialization function initializes 
 *          the memory pool,Divided into two parts,static and dynamic,
 *          the kernel mainly uses static methods.
 * @param   None.
 * @return  [OS_OK] 内存初始化成功。 The memory is initialized successfully.
 * @return  [OS_NOK] 内存初始化失败。The memory initialization failed.
**/
os_err_t AntOS_Mem_Init(void)
{

	G_MemPool.MemBlkCnt = 0;
	G_MemPool.ReLength = OS_MEM_POOL_SIZE;
    G_MemPool.ReMemAddr = G_MemPool.Pool;
	G_MemPool.ReInfoAddr = &G_MemPool.Pool[OS_MEM_POOL_SIZE];
	
	return OS_OK;
}


/**
 * @brief      动态申请内存函数。
 * @details    Dynamic application memory function.
 * @param[in]  ptr 要申请内存的指针。The pointer to apply for memory.
 * @param[in]  size 内存大小。 memory size.
 * @return     [address] 内存申请成功，返回内存块首地址给ptr指针。
 *             The memory application is successful, 
 *             and the first address of the memory block is returned for ptr.
 * @return     [OS_NULL] 内存申请失败，返回NULL空指针给ptr指针。
 *             The memory application is failed, 
 *             and the NULL pointer is returned for ptr.
 * @note       分配内存的过程分两步：
 *             1.先从内存池的顶部获取一段空间存放内存块信息头，方向是向下生成；
 *             2.再从内存池的底部获取一段空间存放内存块储存区，方向是向上生长。
 *	           The process of allocating memory is divided into two steps:
 *             1. First obtain a space from the top of the memory pool to store 
 *             the memory block information header, and the direction is to generate downwards;
 *             2. Get a space from the bottom of the memory pool to store the memory block storage area, 
 *             and the direction is to grow upward.
 *        	   Icon:
 *
 *             ----------------                                    ----------------
 *             | 内存池-信息头 |                                    | 内存池-信息头 | 
 *             ----------------                                    ----------------
 *             | 内存块1存储区 |                                    | 内存块1存储区 | 
 *             ----------------                                    ---------------- 
 *             |              |           分配一块内存              | 内存块2存储区 | 
 *             ----------------          ============>             ---------------- 
 *             |              |                                    |              |
 *             ----------------                                    ----------------
 *             |              |                                    |              |
 *             ----------------                                    ----------------
 *             |              |                                    | 内存块2信息头 |
 *             ----------------                                    ----------------
 *             | 内存块1信息头 |                                    | 内存块1信息头 | 
 *             ----------------                                    ---------------- 
**/
void *OS_Malloc(void *mptr,os_size_t msize)
{
	os_uint8_t *ptr = mptr;
	
	OS_ENTER_CRITICAL();
	
	if (OS_MEM_POOL_SIZE > 10 && msize < OS_MEM_POOL_SIZE)
	{
		/** 更新内存池的内存块信息头地址，向低地址移动 */
		ADDR_LOW_OFFSET(G_MemPool.ReInfoAddr,MEM_BLOK_INFO_SIZE);
		
		/** 更新内存块信息头 */
		((struct Mem_Block *)G_MemPool.ReInfoAddr)->MalPtr = mptr;
		((struct Mem_Block *)G_MemPool.ReInfoAddr)->IndexAddr = G_MemPool.ReMemAddr;
		((struct Mem_Block *)G_MemPool.ReInfoAddr)->Length = msize;
		
		ptr = G_MemPool.ReMemAddr;	/*!< 获取内存块存储区首地址 */
		
		ADDR_HIGH_OFFSET(G_MemPool.ReMemAddr,msize);
		
		/** 更新内存池信息头 */
		G_MemPool.ReLength -= (MEM_BLOK_INFO_SIZE + msize);
		G_MemPool.MemBlkCnt += 1;
		
		OS_EXIT_CRITICAL();
		
		return ptr;
	}
	
	OS_EXIT_CRITICAL();
	
    return OS_NULL;
}


/**
 * @brief      释放内存函数,支持内存碎片整理。
 * @details    Free memory function, Does support memory defragmentation.
 * @param[in]  ptr 要释放内存的指针。The pointer to free for memory.
 * @return     [OS_OK] 内存释放成功。
 *             The memory free is successful.
 * @return     [OS_NOK] 内存释放失败。
 *             The memory free is failed.
 * @note       内存块被释放以后，排在这个内存块后面的内存块，会被全部移动，
 *             以填补空出来的位置，同时再更新被移动后的所有内存块的信息头，
 *             关键点是，更新处理信息头里面申请内存块的指针为被移动后的地址。
 * 			   After the memory block is released, 
 *             all the memory blocks behind this memory block will be moved.
 * 			   To fill in the vacant positions, and at the same time update 
 *             the information headers of all memory blocks that have been moved,
 *             The key point is that the pointer of the application memory block 
 *             in the update processing information header is the address after being moved.
 *        	   Icon:
 *
 *             ----------------                                    ----------------
 *             | 内存池-信息头 |                                    | 内存池-信息头 | 
 *             ----------------                                    ----------------
 *             | 内存块1信息头 |                                    | 内存块2信息头 | 
 *             ----------------                                    ---------------- 
 *             | 内存块2信息头 |            释放内存块1             |               | 
 *             ----------------          ============>             ---------------- 
 *             |              |                                    |              |
 *             ----------------                                    ----------------
 *             |              |                                    |              |
 *             ----------------                                    ----------------
 *             | 内存块2信息头 |                                    |              |
 *             ----------------                                    ----------------
 *             | 内存块1存储区 |                                    | 内存块2存储区 | 
 *             ----------------                                    ---------------- 
**/
os_err_t OS_Free(void *mptr)
{
	os_uint16_t index,msize;
	os_uint8_t *reInfoAddr;
	
	OS_ENTER_CRITICAL();
	
	if (G_MemPool.MemBlkCnt > 0 && mptr != OS_NULL)
	{
		/** 获取当前空闲内存块信息头首地址，用于遍历申请过的内存块 */
		reInfoAddr = G_MemPool.ReInfoAddr;
		
		for (index = 1; index <= G_MemPool.MemBlkCnt; index++)
		{
			/** 查找内存块，条件是对比被申请的内存地址 */

			if (((struct Mem_Block *)reInfoAddr)->MalPtr == mptr)
			{
				/** 获取内存块的大小 */
				msize = ((struct Mem_Block *)reInfoAddr)->Length;
				
				((struct Mem_Block *)(reInfoAddr+8))->MalPtr = OS_NULL;
				((struct Mem_Block *)(reInfoAddr+8))->IndexAddr = OS_NULL;
				((struct Mem_Block *)(reInfoAddr+8))->Length = 0;
				
				/** 最后更新内存池信息 */
				G_MemPool.MemBlkCnt -= 1;
				G_MemPool.ReLength  += (msize + MEM_BLOK_INFO_SIZE);
				
				OS_EXIT_CRITICAL();
				return OS_OK;       
			}
			
			//ADDR_HIGH_OFFSET(G_MemPool.ReInfoAddr,MEM_BLOK_INFO_SIZE);
			//ADDR_LOW_OFFSET(G_MemPool.ReMemAddr,msize);

		}
		/** 每循环一次，内存块信息头地址偏移8个字节（这是信息头的字节空间长度）*/
		ADDR_HIGH_OFFSET(reInfoAddr,MEM_BLOK_INFO_SIZE);
	}
	
	OS_EXIT_CRITICAL();
	return OS_NOK;
}


/**
 * @brief      发送要被回收内存块的申请内存指针的地址。
 * @details    The address of the requested memory pointer to send the memory block to be reclaimed.
 * @param[in]  mptr 申请内存指针的地址。Address of the memory pointer.
 * @return     [OS_OK] 地址发送成功。
 *             The address was sent successfully.
 * @return     [OS_NOK] 地址发送失败。
 *             Failed to send the address.
**/
os_err_t OS_Mem_Send_Recycle_Addr(void *mptr)
{
    if (G_RecMem_Collector.Num < OS_THREAD_MAX_NUM)
    {
        G_RecMem_Collector.MPtr[G_RecMem_Collector.Num++] = mptr;
        return OS_OK;
    }
    return OS_NOK;
}

/**
 * @brief     线程4的入口函数。
 * @details   Entry function of thread 5.
 * @param[in] param 线程5的入口参数。
 * @return    None
**/
os_err_t OS_Mem_Recycle(void)
{
    while(G_RecMem_Collector.Num)
    {
        if(OS_Free(G_RecMem_Collector.MPtr[--G_RecMem_Collector.Num]) != OS_OK)
        {
            return OS_NOK;
        }
    }
    return OS_OK;
}
/*------------------------------------------------------------------------
|                    END OF FLIE.  (C) COPYRIGHT zeweni                  |
------------------------------------------------------------------------*/